#include "Statistics.h"
//#define DEBUG 1

Statistics::Statistics()

{
	PartitionNum=0;
}

Statistics::Statistics(Statistics &copyMe)
{
	//deep copy
	PartitionNum = copyMe.GetPartitionNumber();

	map <string, relation> * tempStat = copyMe.GetRelationMap();

	for (map <string, relation>::iterator relItr = tempStat->begin() ;relItr  != tempStat->end(); relItr ++)
	{

		relation rel;
		rel.numTuples = relItr->second.numTuples;
		rel.numPartition = relItr->second.numPartition;
		for (map <string, long  >::iterator attrItr = relItr->second.Attr.begin();attrItr != relItr->second.Attr.end();attrItr++)
		{

			rel.Attr[attrItr->first] = attrItr->second;
		}

		RelationMap[relItr->first] = rel;
	}


	map<string, vector <string> > *tempattrMap = copyMe.GetAttrMap();
	map<string, vector <string> >::iterator attsMapItr = tempattrMap->begin();
	for (map<string, vector <string> >::iterator attsMapItr = tempattrMap->begin(); attsMapItr != tempattrMap->end(); attsMapItr ++)
	{
		vector<string> relationVec;
		vector<string> * attrVect = &attsMapItr ->second;
		for (int i = 0; i < attrVect->size(); i++)
		{
			relationVec.push_back(attrVect->at(i));
		}


		attrMap[attsMapItr ->first] =relationVec;
	}

	map <int, vector<string> > * pInfo = copyMe.GetPartitionMap();
	map <int, vector<string> >::iterator partItr;
	for (partItr = pInfo ->begin();partItr  != pInfo ->end(); partItr++)
	{
		vector<string> relNames;
		vector<string> * vecrel = &partItr ->second;
		for (int i = 0; i < vecrel->size(); i++)
		{
			relNames.push_back(vecrel->at(i));
		}


		PartitionMap[partItr ->first] = relNames;
	}

}

Statistics::~Statistics()
{
}

void Statistics::AddRel(char *relName, int numTuples)
{

	map <string, relation>::iterator relItr;      
	relItr= RelationMap.find(string(relName));         
	if (relItr == RelationMap.end())       
	{       
		relation rel;              
		rel.numTuples = numTuples;               
		RelationMap[string(relName)] = rel;        
	}  
	else 

	{            
		relItr->second.numTuples = numTuples;        
	} 
}

void Statistics::AddAtt(char *relName, char *attName, int numDistincts)
{

	map <string, relation>::iterator relItr;    
	relItr= RelationMap.find(string(relName));     
	if (relItr == RelationMap.end())   
	{               
		relation rel;               
		rel.Attr[string(attName)] = numDistincts;           
		RelationMap[string(relName)] = rel;         
		vector<string> relationVect;      
		relationVect.push_back(string(relName));     
		attrMap[string(attName)] =relationVect;  
	}     else    {              
		map <string,  long>::iterator attItr;  
		attItr = (relItr->second).Attr.find(string(attName));  
		if (attItr == (relItr->second).Attr.end())             
		{
			(relItr->second).Attr[string(attName)] = numDistincts;                         
			vector<string>relationVect ;           
			relationVect.push_back(string(relName));                 
			attrMap[string(attName)] = relationVect;                
		}               
		else                           
			attItr->second = numDistincts;  
	} 

}

void Statistics::CopyRel(char *oldName, char *newName)
{
	map <string, relation>::iterator relItr;
	map <string, vector <string> >::iterator tempAttsItr;   
	relItr = RelationMap.find(string(oldName));
	relation  *oldRelation = &(relItr->second);
	relation  rel; 
	rel.numTuples = oldRelation->numTuples;
	map <string,  long >::iterator attItr; 
	for (attItr = oldRelation->Attr.begin();attItr != oldRelation->Attr.end(); attItr++) 
	{
		rel.Attr[attItr->first] = attItr->second;    
		tempAttsItr =attrMap .find(attItr->first); 

		if (tempAttsItr == attrMap.end())    
		{         
			cout   << " not found in existing attribute table\n";         
			return;    
		} 
		(tempAttsItr->second).push_back(string(newName));
		RelationMap[string(newName)] = rel;

	}  
}


void Statistics::Read(char *fromWhere)
{
	FILE * file = fopen(fromWhere, "r");
	if (file == NULL)
		return;

	char buffer[128];
	string relName, attName;
	long  numDistValues;

	map <string, vector <string> >::iterator attrItr;

	while (fscanf(file, "%s", buffer) != EOF)
	{
		if (strcmp(buffer, "relstart") == 0)
		{
			relation rel;
			fscanf(file, "%s", buffer);
			relName = buffer;
			fscanf(file, "%lu", &rel.numTuples);
			fscanf(file, "%s", buffer);
			while (strcmp(buffer, "relstop") != 0)
			{
				attName = buffer;
				fscanf(file, "%lu", &numDistValues);
				rel.Attr[attName] = numDistValues;


				attrItr = attrMap.find(string(attName));
				if (attrItr ==attrMap.end()) 
				{
					vector<string> relationVect;
					relationVect.push_back(string(relName));
					attrMap[string(attName)] = relationVect;
				}
				else 
					(attrItr->second).push_back(string(relName));


				fscanf(file, "%s", buffer);
			}
			RelationMap[relName] = rel;
		}
	} 

}

void Statistics::Write(char *fromWhere)
{
	FILE * file = fopen(fromWhere, "w");  
	map <string, relation>::iterator relItr;        
	map <string,  long >::iterator tempattrItr;    

	for (relItr = RelationMap.begin(); relItr != RelationMap.end();relItr++)      
	{                 



		fprintf(file, "\nrelstart\n");                 
		fprintf(file , "\n%s", relItr->first.c_str());       
		relation * rel = &(relItr->second);         
		fprintf(file, "\n%lu",rel->numTuples);       
		for (tempattrItr = rel->Attr.begin();            
				tempattrItr != rel->Attr.end(); tempattrItr++)     
		{                      
			fprintf(file, "\n%s", tempattrItr->first.c_str());            
			fprintf(file, " %lu", tempattrItr->second);      
		}          
		fprintf(file, "\nrelstop\n");


	}                
	// close the file   
	fclose(file); 

}

bool Statistics::CheckErrors(struct AndList *someParseTree, char *relNames[], 
		int numToJoin)
{
	//clear the vector in the begining
	relationSet.clear();
	cnfAtts.clear();
	int columnPrefix;
	string rel;
	string col;
	//check the given relations exists in the RelationMap
	for (int i = 0; i < numToJoin; i++)
	{
		if(RelationMap.find(relNames[i]) == RelationMap.end())
			return false;
	}


	map<string, vector<string> >::iterator colItr;

	//parse tree and put it in cnfAtts  lcode,lvalue,code,rcode,rvalue,marker(OR,AND), use this in estimate to check for next marker
	AndList* parseTree = someParseTree;

	while(parseTree != NULL)
	{
		OrList* theOrList = parseTree->left;
		while(theOrList != NULL) 
		{
			ComparisonOp* compOp = theOrList->left;


			int lcode = compOp->left->code;
			string lval = compOp->left->value;
			stringstream ss;
			ss << lcode;


			cnfAtts.push_back(ss.str());
			cnfAtts.push_back(lval);
			ss<<compOp->code;

			cnfAtts.push_back(ss.str());


			int rcode= compOp->right->code;
			string rval= compOp->right->value;

			ss << rcode;

			cnfAtts.push_back(ss.str());
			cnfAtts.push_back(rval); 
			//check for the validity of the relation name if given in cnf like relation.x
			if(lcode == NAME) 
			{
				columnPrefix= lval.find(".");
				if (columnPrefix != string::npos) 
				{
					rel = lval.substr(0, columnPrefix);
					col = lval.substr(columnPrefix+ 1);
					colItr = attrMap.find(col);
					if (colItr ==attrMap.end()) 
						return false;
				}
				else
				{
					//simply present as x
					col= lval;
					colItr = attrMap.find(col);
					if (colItr == attrMap.end()) 
						return false;

					//if without relation name, the attribute should be unique
					if ((colItr->second).size() > 1)
						return false; 
					else
						rel = (colItr->second).at(0);
				}
				relationSet.push_back(rel);
			}
			if(rcode== NAME) 
			{

				columnPrefix= rval.find(".");
				if (columnPrefix!= string::npos) 
				{
					rel = rval.substr(0, columnPrefix);
					col= rval.substr(columnPrefix+ 1);
					colItr = attrMap.find(col);
					if (colItr == attrMap.end()) 
						return false;
				}
				else
				{
					col= rval;
					colItr = attrMap.find(col);
					if (colItr == attrMap.end())
						return false;


					if ((colItr->second).size() > 1)
						return false; 
					else
						rel = (colItr->second).at(0);
				}
				relationSet.push_back(rel);
			}

			//extend orlist
			if(theOrList->rightOr != NULL)
				cnfAtts.push_back("OR");
			theOrList = theOrList->rightOr;
		}
		//extend andlist
		if(parseTree->rightAnd != NULL)
			cnfAtts.push_back("AND");
		else
			cnfAtts.push_back(".");
		parseTree = parseTree->rightAnd;
	}

	//check for mapping btw relations in relation name and partition
	for (int i=0; i<numToJoin; i++)
	{
		rel = relNames[i];
		int partitionNum = RelationMap[rel].numPartition;
		if (partitionNum != -1)
		{

			vector<string> existRelPartition = PartitionMap[partitionNum];
			for (int j = 0; j < existRelPartition .size(); j++)
			{
				string t1 = existRelPartition .at(j);
				bool found = false;
				for (int k = 0; k < numToJoin; k++)
				{
					string t2 = relNames[k];
					if (t1.compare(t2) == 0)
					{
						found = true;
						break;
					}
				} 
				if (found == false)
					return false;
			}
		}
	}

	//check for mapping bet relaion in relation name and relation set built from parsetree
	for (vector <string>::iterator set_itr = relationSet.begin(); set_itr != relationSet.end(); set_itr++)
	{
		string t1 = *set_itr;
		bool found = false;
		for (int k = 0; k < numToJoin; k++)
		{
			string t2 = relNames[k];
			if (t1.compare(t2) == 0)
			{ 
				found = true;
				break;
			}
		}
		if (found == false)
			return false; 
	}


	return true;
}

void  Statistics::Apply(struct AndList *parseTree, char *relNames[], int numToJoin)
{

	double tuples = Estimate(parseTree, relNames, numToJoin);
	if (tuples == -1) 
	{
		cout << "\n estimate wrong \n";
		return;
	}
	else
	{

		if (!CheckErrors(parseTree, relNames, numToJoin))
		{
			cerr << "\nError found in parseTree.\n";
			return;
		}
		else
		{
			int  oldPartition = -1;
			string oldRel;
			map <string, relation>::iterator relItr;
			vector <string>::iterator vectItr ;
			//check for partition of all relation in relation set
			for (vectItr = relationSet.begin(); vectItr != relationSet.end(); vectItr++)
			{
				relItr = RelationMap.find(*vectItr);
				if (relItr == RelationMap.end())
				{
					cerr << "\n relation not found in Statistics object \n";
					return;
				} 
				if ((relItr->second).numPartition != -1)
				{
					oldPartition = (relItr->second).numPartition;
					break;
				}
			}
			//if no relation has partition number other than -1,create a new partition and increase the partition num of statistics object
			if (oldPartition == -1)
			{
				PartitionNum++;
				vector <string> relNames;
				for (vectItr = relationSet.begin(); vectItr != relationSet.end(); vectItr++)
				{
					relItr = RelationMap.find(*vectItr);
					(relItr->second).numPartition = PartitionNum;
					(relItr->second).numTuples = (double)tuples;


					relNames.push_back(*vectItr);
				}

				PartitionMap[PartitionNum] = relNames;
			}

			//otherwise, take all relations from a existing partition and add relationset+partitionset into new partition,update tuples
			else 
			{

				vector <string> oldRelationNames = PartitionMap[oldPartition];
				for (int i=0; i<oldRelationNames.size(); i++)

					relationSet.push_back(oldRelationNames.at(i));

				oldRelationNames.clear();
				for (vectItr = relationSet.begin(); vectItr != relationSet.end(); vectItr++)
				{
					relItr = RelationMap.find(*vectItr);
					(relItr->second).numPartition = oldPartition;
					(relItr->second).numTuples = (long )tuples;

					oldRelationNames.push_back(*vectItr);
				}

				PartitionMap[oldPartition] = oldRelationNames;
			}
		}
	}
}

double Statistics::Estimate(struct AndList *parseTree, char **relNames, int numToJoin)
{
	if(CheckErrors(parseTree,relNames,numToJoin) == false)
		return -1;

	numTuples *numTuple = new numTuples[numToJoin];
	for(int i=0;i< numToJoin;i++){
		numTuple[i].deleted = false;
		map<string,relation>::iterator tempItr;
		tempItr = RelationMap.find(string(relNames[i]));
		numTuple[i].value = new double*();
		*(numTuple[i].value) = new double(tempItr->second.numTuples);
		numTuple[i].partition = tempItr->second.numPartition;  
		#ifdef DEBUG
		cout << " I " << i << " Rel : " << relNames[i] <<  " Value: " << **(numTuple[i].value) << "\n";
		#endif
	}
	for(int i=0;i<numToJoin;i++){
		if(numTuple[i].partition == -1 || numTuple[i].deleted ==true)
			continue;
		for(int j=0;j<numToJoin;j++){
			if((i!=j) && numTuple[i].partition == numTuple[j].partition){
				if(numTuple[j].deleted == false){
					delete numTuple[j].value;
                                	numTuple[j].deleted = true;
                                	numTuple[j].value = numTuple[i].value;
				}
			}
		}
	}
	map<string,double> attrDistinctVal;
	int leftDistinct;

	while(parseTree != NULL)
        {
                OrList* theOrList = parseTree->left;
		attrDistinctVal.clear();
		int leftIndex = -1;
                while(theOrList != NULL)
                {
                        ComparisonOp* compOp = theOrList->left;
			double leftTuples=0,leftDistinct=0,rightTuples=0,rightDistinct=0;
			string leftRel, rightRel;
			string leftVal, rightVal;
			if(compOp->left->code == NAME){
				string lval = compOp->left->value;
                                int columnPrefix = lval.find(".");
                                map<string,relation>::iterator relMap;
				leftVal = string(compOp->left->value);
                                //If the attribute is given as Relation.attribute
                                if (columnPrefix != string::npos)
                                {
                                        leftRel = lval.substr(0, columnPrefix);
					leftVal = leftVal.substr(columnPrefix+1);
				
                                }
                                else
                                {
                                        map<string,relation>::iterator relMap;
                                        vector<string> relationVect;
                                        relationVect = attrMap[string(lval)];
                                        leftRel = relationVect[0];
                                }
				#ifdef DEBUG
				cout << "Left Rel " << leftRel << " Attr " << leftVal << "\n";
				#endif
                                relMap = RelationMap.find(leftRel);
				for(int i=0; i<numToJoin; i++){
                                        if(strcmp(relNames[i],leftRel.c_str()) == 0){
						leftTuples = **(numTuple[i].value);
						leftIndex = i; 
					}
				}
                                leftDistinct = relMap->second.Attr[leftVal];
			}

			if(compOp->right->code == NAME){
				string  rval = compOp->right->value;
                                int columnPrefix = rval.find(".");
                                map<string,relation>::iterator relMap;
				rightVal = string(compOp->right->value);
                                //If the attribute is given as Relation.attribute
                                if (columnPrefix != string::npos)
                                {
                                        rightRel = rval.substr(0, columnPrefix);
					rightVal = rightVal.substr(columnPrefix+1);
                                }
                                else
                                {
                                        map<string,relation>::iterator relMap;
                                        vector<string> relationVect;
                                        relationVect = attrMap[string(rval)];
                                        rightRel = relationVect[0];
                                }
				#ifdef DEBUG
				cout << "Right Rel " << rightRel << " Attr " << rightVal <<  "\n";
				#endif
                                relMap = RelationMap.find(rightRel);
				for(int i=0; i<numToJoin; i++){
                                        if(strcmp(relNames[i],rightRel.c_str()) == 0){
                                                rightTuples = **(numTuple[i].value);
                                        }
                                }
                                rightDistinct = relMap->second.Attr[rightVal];
			}
			
			//Case: Join
			if(compOp->left->code == NAME && compOp->right->code == NAME){
				#ifdef DEBUG
				cout << "Left " << leftTuples << " Right " << rightTuples << " LeftDis " << leftDistinct << " RightDis " << rightDistinct << "\n";
				#endif
				double newTuple = (leftTuples * rightTuples) / max(leftDistinct,rightDistinct);
				int keep = -1;
				for(int i=0; i< numToJoin; i++){
					if((strcmp(relNames[i],leftRel.c_str()) == 0) || (strcmp(relNames[i],rightRel.c_str()) == 0)){
						if(keep != -1){
							if(numTuple[i].deleted == false){
								delete *(numTuple[i].value);
								numTuple[i].deleted = true;
							}
							*(numTuple[i].value) = *(numTuple[keep].value); 
						} else {
							*(*(numTuple[i].value)) = newTuple;
							keep = i;
						}
					}
				}
			} else if (compOp->right->code != NAME){
				double value;
				if(compOp->code != EQUALS){
					value = (double)1/(double)3; 
				} else {
					value = 1/(double)leftDistinct;
				}
				#ifdef DEBUG
				cout << "val in <>= loop  " << value << "\n";
				#endif
				map<string,double>::iterator temp;
				temp = attrDistinctVal.find(string(compOp->left->value));
				if(temp == attrDistinctVal.end()){
					attrDistinctVal[string(compOp->left->value)] = value;
				} else {
					temp->second = temp->second + value;
				}
			}
			theOrList = theOrList->rightOr;
		}

		map<string,double>::iterator temp;
		double sum=0,product=1;
		int numberOfAtts = 0;
		for(temp = attrDistinctVal.begin(); temp!= attrDistinctVal.end(); temp++){
			sum = sum + temp->second;
			product = product * temp->second;
			numberOfAtts++;
		#ifdef DEBUG
		cout << " Loop Sum is: " << sum << " Product: " << product <<  "\n";
		#endif
		}
		#ifdef DEBUG
		cout << "Sum is: " << sum << " Product: " << product <<  "\n";
		#endif
		if(numberOfAtts!=0) {
			if(numberOfAtts > 1)
				sum = sum - product;

			*(*(numTuple[leftIndex].value)) = sum*(*(*(numTuple[leftIndex].value)));
		}
		#ifdef DEBUG
		cout << "Inside loop " <<  **(numTuple[leftIndex].value) << "\n";
		#endif

		parseTree = parseTree->rightAnd;
	}
	#ifdef DEBUG
	cout << "Value: " <<  *(*(numTuple[0].value)) << "\n";
	#endif
	return *(*(numTuple[0].value));
}

